package org.jeecg.crud.sys.asyncwithcallback;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.util.spelutil.SpELUtils;
import org.jeecg.crud.sys.asyncwithcallback.biz_async_task.po.AsyncTask;
import org.jeecg.crud.sys.asyncwithcallback.biz_async_task.service.AsyncTaskService;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import static cn.hutool.core.text.CharSequenceUtil.isBlank;

@Aspect
@Component
@RequiredArgsConstructor
public class AsyncWithCallbackAspect {
    private final AsyncTaskService asyncTaskService;

    // ... （省略其他方法）
    public static String generateBeanMethodSpel(JoinPoint joinPoint, String beanName) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        StringBuilder spelBuilder = new StringBuilder("@");

        // 拼接bean名
        spelBuilder.append(beanName).append(".");

        // 使用方法名和参数构建 SpEL 表达式
        spelBuilder.append(methodName).append("(");
        for (int i = 0; i < args.length; i++) {
            if (i > 0) {
                spelBuilder.append(",");
            }
            spelBuilder.append("#args[").append(i).append("]");
        }
        spelBuilder.append(")");

        return spelBuilder.toString();
    }


//}


    /**
     * 直接短路原方法为异步任务并记录到数据库
     *
     * @param joinPoint
     * @param asyncWithCallback 入参
     * @return {@link Object }
     * @author zjarlin
     * @since 2023/12/11
     *///    @Around("@annotation(asyncWithCallback) && (execution(void *.*(..)) || execution(java.util.concurrent.Future *.*(..)))")
    @Around("@annotation(asyncWithCallback)")
    public Object aroundAsyncWithCallback(ProceedingJoinPoint joinPoint, AsyncWithCallback asyncWithCallback) {
        Class<?> currentReturnTye = ((MethodSignature) joinPoint.getSignature()).getReturnType();
        boolean primitive = currentReturnTye.isPrimitive();
        if (primitive) {
            throw new RuntimeException("该异步注解只支持返回引用数据类型的方法上!");
        }
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String arguments = Arrays.toString(args);
        String callerBean = joinPoint.getTarget().getClass().getSimpleName();
        callerBean = StrUtil.lowerFirst(callerBean);
//        callerBean = StrUtil.addPrefixIfNot(callerBean, "@");

        String returnType = currentReturnTye.getName();

        String callbackSpel = asyncWithCallback.callbackSpel();

        // 保存任务信息到数据库表
        AsyncTask task = new AsyncTask();
        String snowflakeNextIdStr = IdUtil.getSnowflakeNextIdStr();
        task.setBizComment(Optional.ofNullable(asyncWithCallback.value()).filter(StrUtil::isNotBlank).orElse("任务" + snowflakeNextIdStr));
        task.setCurrentReturnTypeClass(currentReturnTye);
        task.setCallBackReturnType(asyncWithCallback.callBackReturnType());
        task.setTaskStartTimestamp(System.currentTimeMillis());
        task.setMethodName(methodName);
        task.setMethodParameters(arguments);
        task.setCallerBean(callerBean);
        task.setReturnType(returnType);
        String currentSpElExpression = generateBeanMethodSpel(joinPoint, callerBean);
        task.setCurrentSpElExpression(currentSpElExpression);
        task.setCallbackSpElExpression(callbackSpel);
        task.setTaskStatus(TaskStatus.ING);
        asyncTaskService.save(task);
        String id = task.getId();
        ThreadLocalUtil.set(id);

        //自定义线程池默认4
        Executor executor = Executors.newFixedThreadPool(asyncWithCallback.numberOfThreads());
        CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
            Object proceed;
            try {
                proceed = joinPoint.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
            return proceed;
        }, executor);
        future.whenComplete((result, throwable) -> {
            if (throwable != null) {
                // 异常处理
                throwable.printStackTrace();
                task.setTaskStatus(TaskStatus.ERROR);
            } else {
                // 异步方法执行完成后调用回调函数
                task.setTaskEndTimestamp(System.currentTimeMillis());
                //设置任务状态为完成
                task.setTaskStatus(TaskStatus.COMPLETED);
                Pair<String, String> stringStringPair = invokeCallback(result, task);
                String currRes = stringStringPair.getLeft();
                String callbackRes = stringStringPair.getRight();
                task.setCurrResult(currRes);
                task.setCallbackResult(callbackRes);
            }
            asyncTaskService.saveOrUpdate(task);
            //todo SSE通知
        });
        return null;
    }

    /**
     * @param result 作为callback的入参
     * @param task   未来任务
     * @return
     * @author zjarlin
     * @since 2023/12/11
     */

    private Pair<String, String> invokeCallback(Object result, AsyncTask task) {
        String callbackSpElExpression = task.getCallbackSpElExpression();
        String jsonString = JSON.toJSONString(result);
        if (isBlank(callbackSpElExpression)) {
            return Pair.of(jsonString, null);
        }
        AbstractMap<String, Object> spelCtxMap = new HashMap<>();
        if (Objects.nonNull(result)) {
            Class<?> currentReturnTypeClass = task.getCurrentReturnTypeClass();
            Object convert = Convert.convert(currentReturnTypeClass, result);
            spelCtxMap.put("result", convert);
        }
        Class<?> callBackReturnType = task.getCallBackReturnType() == null ? Object.class : task.getCallBackReturnType();
        Object o = SpELUtils.evaluateExpression(spelCtxMap, callbackSpElExpression, callBackReturnType);
        task.setCallbackEndTimestamp(System.currentTimeMillis());
        //     耗时任务的返回+  回调返回
        String resJson = jsonString;
        String callbackJson = JSON.toJSONString(o);
        return Pair.of(resJson, callbackJson);
    }


}
